using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows.Input;
using Microsoft.Practices.Composite.Presentation.Commands;
using AG.Infrastructure.Utility;

namespace VocabCards
{
    public class WordProperties
    {
        public WordProperties()
        {
            _definitions = new List<Definition>();
        }

        private bool _denormalize;
        public bool Denormalize
        {
            get { return _denormalize; }
            set
            {
                _denormalize = value;
                if (_posDefinitions != null)
                    _posDefinitions.ForEach(definitions =>
                                                {
                                                    definitions.Denormalize = value;
                                                    definitions.ExtractWordStrings();
                                                });
            }
        }

        public string Word { get; set; }
        private List<Definition> _definitions;
        public List<Definition> Definitions
        {
            get { return _definitions; }
        }

        private List<PosDefinitions> _posDefinitions;

        public List<PosDefinitions> PosDefinitions
        {
            get
            {
                return _posDefinitions
                    ?? (Denormalize ? (_posDefinitions = CreatePosDefinitions()) : null);
            }
        }

        private string _synonyms;
        public string Synonyms
        {
            get
            {
                return _synonyms ??
                    (Denormalize ? (_synonyms = CreateString(d => d.Synonyms)) : null);
            }
        }

        private ObservableCollection<StringHolder> _synList;
        public ObservableCollection<StringHolder> SynList
        {
            get
            {
                if (!Denormalize)
                    return new ObservableCollection<StringHolder>();

                if (_synList != null)
                    return _synList;

                return (_synList = CreateSynOrAntList(Synonyms, "synonyms: "));
            }

        }

        //todo:  refactor AsStrings, SynList, AntList to object which supports 
            //reversion:  undo or showing all if you hid too many
            //maybe it will even support saving preferences in future


        private ObservableCollection<StringHolder> _antList;
        public ObservableCollection<StringHolder> AntList
        {
            get
            {
                if (!Denormalize)
                    return new ObservableCollection<StringHolder>();

                if (_antList != null)
                    return _antList;

                return (_antList = CreateSynOrAntList(Antonyms, "antonyms: "));
            }

        }

        /// <summary>
        /// splits a string based on comma and returns obs coll
        /// </summary>
        /// <param name="wordList"></param>
        /// <param name="heading"></param>
        /// <returns></returns>
        private ObservableCollection<StringHolder> CreateSynOrAntList(string wordList, string heading)
        {
            var syns = wordList.Split(',');
                
            if (syns.Length == 0)
                return new ObservableCollection<StringHolder>();

            var list = syns.Select(s => s.Trim() + ", ").ToList();
            list.Insert(0, heading);

            var retList = list.ToStringHolders(true);

            retList.First().IsHeading = true;

            return retList;
        }

        private string _antonyms;
        public string Antonyms
        {
            get
            {
                return _antonyms ??
                    (Denormalize ? (_antonyms = CreateString(d => d.Antonyms)) : null);
            }
        }

        public void ResetSynAnt(int maxSynAnt)
        {
            _maxSynAnt = maxSynAnt;

            _antonyms = null;
            _synonyms = null;
        }

        private int _maxSynAnt = int.MaxValue;

        string CreateString(Func<Definition, IEnumerable<string>> selector)
        {

            var uqStrings = new Dictionary<string, string>();
            var builder = new StringBuilder();

            int selections = 0;

            _definitions.ForEach(d =>
                {
                    if (selector(d) != null)
                        foreach (var s in selector(d))
                        {
                            if (!uqStrings.ContainsKey(s) && s != Word && selections < _maxSynAnt)
                            {
                                selections++;
                                uqStrings.Add(s, s);

                                if (builder.Length > 0)
                                {
                                    builder.Append(", ");
                                }

                                builder.Append(s);
                            }
                        }

                }
                );

            return builder.ToString();
        }

        private List<PosDefinitions> CreatePosDefinitions()
        {
            var defs = new List<PosDefinitions>();

            var poses = _definitions
                    .Select(definition => definition.PartOfSpeech)
                    .Distinct();


            foreach (var pos in poses)
            {
                var posDef = new PosDefinitions() { Denormalize = Denormalize };
                posDef.PartOfSpeech = pos;

                foreach (var definition in Definitions.Where(d => d.PartOfSpeech == pos))
                {
                    posDef.Definitions.Add(definition);
                }

                defs.Add(posDef);
            }

            return defs;
        }
    }

    public class PosDefinitions
    {
        public PosDefinitions()
        {
            _definitions = new List<Definition>();
            _asStrings = new ObservableCollection<StringHolder>();
        }
        public string PartOfSpeech { get; set; }

        private readonly List<Definition> _definitions;
        public List<Definition> Definitions
        {
            get { return _definitions; }
        }

        private string _asString;
        public string AsString
        {
            get
            {
                if (_asString != null)
                    return _asString;

                _asString = PartOfSpeech + ": ";

                foreach (var definition in Definitions)
                {
                    _asString += definition.Def.Trim() + ". ";
                }

                return _asString;
            }
        }

        public bool Denormalize { get; set; }

        private ObservableCollection<StringHolder> _asStrings;
        public ObservableCollection<StringHolder> AsStrings
        {
            get
            {
                if (!Denormalize)
                    return null;
                
                if (_asStrings.Count > 0 )
                    return _asStrings;

                ExtractWordStrings();

                return _asStrings;

            }
            set { }
        }

        public void ExtractWordStrings()
        {
            var posString = new StringHolder(PartOfSpeech + ": ", -1)
                                {
                                    IsHeading = true,
                                };
            posString.Command = new DelegateCommand<object>(o1 => _asStrings.Remove(posString));

            _asStrings.Add(posString); ;

            int group = 0;

            foreach (var definition in Definitions)
            {
                var g = ++group;

                Action<object> deleteFunc = o =>
                                            _asStrings
                                                .Where(sh => sh.Group == g)
                                                .ToList()
                                                .ForEach(holder => _asStrings.Remove(holder))
                    ;

                var delCmd = new DelegateCommand<object>(deleteFunc);

                definition.Def.Trim()
                    .Split(new[] { ' ' })
                    .ToList()
                    .ForEach(s =>

                             _asStrings.Add(
                                 new StringHolder(s, group)
                                     {
                                         Command = delCmd
                                     }));

                _asStrings.Last().TheString += ". ";

            }
        }

        public override string ToString()
        {
            return Definitions.Count + " " + AsString;
        }
    }

    /// <summary>
    /// allows matching a specific string to a particular definition
    /// </summary>
    public class StringHolder
    {
        public string TheString { get; set; }
        public int Group { get; set; }

        public StringHolder(string theString, int group)
        {
            TheString = theString;
            Group = group;
        }

        public ICommand Command { get; set; }
        public override string ToString()
        {
            return TheString;
        }

        public bool IsHeading { get; set; }
    }

    public static class StringExtension
    {


        public static ObservableCollection<StringHolder> StripLastComma(this ObservableCollection<StringHolder> holder)
        {
            var last = holder.LastOrDefault();

            if (last != null && last.TheString.Trim().EndsWith(","))
            {
                last.TheString = last.TheString.Trim().Substring(0, last.TheString.Trim().Length - 1);

                //trigger change notification:
                holder[holder.Count - 1] = last;
            }

            return holder;
        }

        /// <summary>
        /// converts string to a stringholder obs collection
        /// where each item exists as a singular removable piece
        /// </summary>
        /// <param name="strings"></param>
        /// <param name="stripLastComma"></param>
        /// <returns></returns>
        public static ObservableCollection<StringHolder> ToStringHolders(
            this IEnumerable<string> strings, bool stripLastComma)
        {
            var holder = new ObservableCollection<StringHolder>();

            strings.ForEach(s =>
                    {
                        var hold1 = new StringHolder(s, 0);
                        hold1.Command = new  DelegateCommand<object>(o1 =>
                                 {
                                     holder.Remove(hold1);

                                     if (stripLastComma)
                                     {
                                         holder.StripLastComma();
                                     }
                                 }
                        );
                        holder.Add(hold1);
                    }
                );

            return holder.StripLastComma();
        }

    }

    public class Definition
    {
        public string PartOfSpeech { get; set; }
        public string Def { get; set; }
        public List<string> Synonyms { get; set; }
        public List<string> Antonyms { get; set; }
    }
}